package org.rcgwt.client.ui.grid.renderer;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.rcgwt.client.theme.CssStyle;
import org.rcgwt.client.ui.grid.internal.model.IGroupModel;
import org.rcgwt.client.ui.grid.internal.model.IGroupTitleModel;
import org.rcgwt.client.ui.grid.internal.model.IRowModel;
import org.rcgwt.client.ui.grid.internal.model.IViewModel;
import org.rcgwt.client.ui.grid.internal.model.IViewModelElements;
import org.rcgwt.client.ui.grid.internal.model.IGroupTitleModel.Position;
import org.rcgwt.client.ui.grid.model.IGridWidget;
import org.rcgwt.client.ui.grid.model.IGroup;
import org.rcgwt.client.ui.grid.model.IGroupDataModel;
import org.rcgwt.client.ui.grid.theme.IGridTheme;

import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.TableRowElement;
import com.google.gwt.dom.client.TableSectionElement;

/**
 * Renderer Group and call renderer for child {@link IRenderer} for
 * {@link IRowModel}
 * 
 * @author Olivier Oeuillot
 * 
 */
public class DefaultGroupRenderer extends AbstractRenderer<IGroupModel>
        implements IGroupRenderer {

    private static final String GROUP_TITLE_POSITION_ATTRIBUTE = "_group_row_title";

    private static final String GROUP_TITLE_MODEL_INDEX_ATTRIBUTE = "_group_title_midx";

    private static final int TOP_POSITION = IGroupTitleModel.Position.TOP
            .ordinal() + 1;

    private static final int BOTTOM_POSITION = IGroupTitleModel.Position.BOTTOM
            .ordinal() + 1;

    public Element render(IViewRenderContext viewRenderContext,
            Element parentElement, IGroupModel groupModel, int renderedIndex) {

        TableSectionElement groupElement = parentElement.getOwnerDocument()
                .createTBodyElement();

        IGroupTitleModel groupTitleModels[] = groupModel.listGroupTitleModels();

        if (groupTitleModels != null) {
            renderGroupTitle(viewRenderContext, parentElement, groupModel,
                    IGroupTitleModel.Position.TOP);
        }

        update(viewRenderContext, groupElement, groupModel, renderedIndex);

        if (groupTitleModels != null) {
            renderGroupTitle(viewRenderContext, parentElement, groupModel,
                    IGroupTitleModel.Position.BOTTOM);
        }

        return groupElement;
    }

    protected void renderGroupTitle(IViewRenderContext viewRenderContext,
            Element groupElement, IGroupModel groupModel, Position top) {
        IGroupTitleModel groupTitleModels[] = groupModel.listGroupTitleModels();

        if (groupTitleModels == null) {
            return;
        }

        int renderedIndex = 0;
        for (IGroupTitleModel groupTitleModel : groupTitleModels) {

            if (groupTitleModel.getPosition() != top) {
                continue;
            }

            // Création du titre
            IGroupTitleRenderer groupTitleRowRenderer = viewRenderContext
                    .getGridRenderContext().getTheme().getGroupTitleRenderer(
                            groupTitleModel);
            if (groupTitleRowRenderer == null) {
                continue;
            }

            Element groupTitleRowElement = groupTitleRowRenderer.render(
                    viewRenderContext, groupElement, groupTitleModel,
                    renderedIndex);

            if (groupTitleRowElement == null) {
                continue;
            }

            groupElement.appendChild(groupTitleRowElement);

            groupTitleRowElement.setPropertyInt(GROUP_TITLE_POSITION_ATTRIBUTE,
                    (top == IGroupTitleModel.Position.TOP) ? TOP_POSITION
                            : BOTTOM_POSITION);

            groupTitleRowElement.setPropertyInt(
                    GROUP_TITLE_MODEL_INDEX_ATTRIBUTE, renderedIndex);

            renderedIndex++;
        }

    }

    @Override
    public void update(IViewRenderContext viewRenderContext,
            Element groupElement, IGroupModel groupModel, int renderedIndex) {
        super
                .update(viewRenderContext, groupElement, groupModel,
                        renderedIndex);

        viewRenderContext.getViewModel().getViewModelElements()
                .setGroupElement(groupModel, groupElement, renderedIndex);
    }

    @Override
    protected CssStyle getCssStyle(IViewRenderContext viewRenderContext,
            IGroupModel group) {
        IGridRenderContext gridRenderContext = viewRenderContext
                .getGridRenderContext();

        return gridRenderContext.getTheme().getGroupStyleName(
                gridRenderContext, group);
    }

    @Override
    protected void updateContent(IViewRenderContext viewRenderContext,
            Element groupElement, IGroupModel groupModel) {

        TableSectionElement tableSectionGroupElement = TableSectionElement
                .as(groupElement);

        IGridRenderContext gridRenderContext = viewRenderContext
                .getGridRenderContext();

        IViewModel viewModel = viewRenderContext.getViewModel();
        IViewModelElements viewModelElements = viewModel.getViewModelElements();

        IGridWidget grid = gridRenderContext.getGridModel().getGridWidget();
        IGridTheme theme = gridRenderContext.getTheme();

        viewModelElements
                .clearRowElements(groupModel, tableSectionGroupElement);

        NodeList<TableRowElement> rows = null;
        int rowsLength = 0;
        if (tableSectionGroupElement.hasChildNodes()) {
            rows = tableSectionGroupElement.getRows();
            rowsLength = rows.getLength();
        }

        Element beforeElement = null;

        int rowIndex = 0;
        IGroupTitleModel groupTitleModels[] = groupModel.listGroupTitleModels();

        if (groupTitleModels != null) {
            int groupTitleRenderedIndex = 0;
            for (; rowIndex < rowsLength; rowIndex++) {
                Element rowElement = rows.getItem(rowIndex);

                int groupTitlePosition = rowElement
                        .getPropertyInt(GROUP_TITLE_POSITION_ATTRIBUTE);
                if (groupTitlePosition == 0) {
                    break;
                }

                int titleModelIndex = rowElement
                        .getPropertyInt(GROUP_TITLE_MODEL_INDEX_ATTRIBUTE);

                IGroupTitleModel groupTitleModel = groupTitleModels[titleModelIndex];

                IGroupTitleRenderer groupTitleRenderer = theme
                        .getGroupTitleRenderer(groupTitleModel);

                groupTitleRenderer.update(viewRenderContext, rowElement,
                        groupTitleModel, groupTitleRenderedIndex++);
            }
        }

        IGroupDataModel groupDataModel = grid.getGroupDataModel();

        IGroup group = groupModel.getGroup();

        int groupRowCount = groupDataModel.getGroupRowCount(group);

        Iterator<Integer> rowModelIndexesIterator = groupDataModel
                .getRowModelIndexes(group, 0, groupRowCount);

        for (int renderedIndex = 0; rowModelIndexesIterator.hasNext(); renderedIndex++) {

            int rowModelIndex = rowModelIndexesIterator.next().intValue();

            IRowModel row = groupModel.getRowModel(rowModelIndex);

            Element rowElement = null;
            for (; rowIndex < rowsLength;) {
                rowElement = rows.getItem(rowIndex++);

                int groupTitlePosition = rowElement
                        .getPropertyInt(GROUP_TITLE_POSITION_ATTRIBUTE);
                if (groupTitlePosition == 0) {
                    break;
                }

                if (groupTitlePosition == BOTTOM_POSITION) {
                    beforeElement = rowElement;
                }

                // Decoration TR ???
            }

            if (rowElement != null) {
                // Simply update ...

                updateChild(viewRenderContext, theme, rowElement, row,
                        renderedIndex);
                continue;
            }

            // Create a new one !

            rowElement = renderChild(viewRenderContext, theme,
                    tableSectionGroupElement, row, renderedIndex);

            tableSectionGroupElement.insertBefore(rowElement, beforeElement);
        }

        if (groupTitleModels != null) {
            int groupTitleRenderedIndex = 0;
            for (; rowIndex < rowsLength; rowIndex++) {
                Element rowElement = rows.getItem(rowIndex);

                int groupTitlePosition = rowElement
                        .getPropertyInt(GROUP_TITLE_POSITION_ATTRIBUTE);
                if (groupTitlePosition == 0) {
                    break;
                }

                int titleModelIndex = rowElement
                        .getPropertyInt(GROUP_TITLE_MODEL_INDEX_ATTRIBUTE);

                IGroupTitleModel groupTitleModel = groupTitleModels[titleModelIndex];

                IGroupTitleRenderer groupTitleRenderer = theme
                        .getGroupTitleRenderer(groupTitleModel);

                groupTitleRenderer.update(viewRenderContext, rowElement,
                        groupTitleModel, groupTitleRenderedIndex++);
            }
        }

        // Delete unused rowElements
        while (rowIndex < rowsLength) {
            tableSectionGroupElement.deleteRow(rowIndex);
            rowsLength--;
        }
    }

    protected Element renderChild(IViewRenderContext renderContext,
            IGridTheme theme, Element groupElement, IRowModel row,
            int renderedIndex) {

        IRenderer<IRowModel> rowRenderer = theme.getRowRenderer(row);

        assert rowRenderer != null : "No rowRenderer computed for row '" + row
                + "'.";

        Element rowElement = rowRenderer.render(renderContext, groupElement,
                row, renderedIndex);

        return rowElement;
    }

    protected void updateChild(IViewRenderContext viewRenderContext,
            IGridTheme theme, Element rowElement, IRowModel row,
            int renderedIndex) {

        IRenderer<IRowModel> rowRenderer = theme.getRowRenderer(row);

        assert rowRenderer != null : "No rowRenderer computed for row '" + row
                + "'.";

        rowRenderer.update(viewRenderContext, rowElement, row, renderedIndex);
    }

    @Override
    public Element[] listRowsElement(IViewRenderContext renderContext,
            Element groupElement, IGroupModel group) {

        // Permet de recalculer les renderedIndexes !

        TableSectionElement tbody = TableSectionElement.as(groupElement);

        NodeList<TableRowElement> nodeList = tbody.getRows();
        int nodeListSize = nodeList.getLength();

        List<Element> elements = new ArrayList<Element>(nodeListSize);
        for (int i = 0; i < nodeListSize; i++) {
            TableRowElement tr = nodeList.getItem(i);

            if (tr.getPropertyInt(GROUP_TITLE_POSITION_ATTRIBUTE) != 0) {
                continue;
            }

            elements.add(tr);
        }

        return elements.toArray(new Element[elements.size()]);
    }
}
